home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Games of Daze
/
Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso
/
gnuish
/
mkinf10
/
makeinfo.e
< prev
next >
Wrap
Text File
|
1990-11-01
|
40KB
|
1,817 lines
/* Makeinfo -- convert texinfo format files into info files
Copyright (C) 1987 Free Software Foundation, Inc.
This file is part of GNU Info.
Makeinfo is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY. No author or distributor accepts
responsibility to anyone for the consequences of using it or for
whether it serves any particular purpose or works at all, unless he
says so in writing. Refer to the GNU Emacs General Public License
for full details.
Everyone is granted permission to copy, modify and redistribute
Makeinfo, but only under the conditions described in the GNU Emacs
General Public License. A copy of this license is supposed to
have been given to you along with GNU Emacs so you can know your
rights and responsibilities. It should be in a file named COPYING.
Among other things, the copyright notice and this notice must be
preserved on all copies. */
/* MS-DOS port (c) 1990 by Thorsten Ohl, td12@ddagsi3.bitnet
This port is also distributed under the terms of the
GNU General Public License as published by the
Free Software Foundation.
Please note that this file is not identical to the
original GNU release, you should have received this
code as patch to the official release.
$Header: e:/gnu/info/RCS/makeinfo.e 0.1.1.1 90/10/05 11:25:42 tho Exp $
*/
/* cont'd from makeinfo.c */
/*===(cut here)===*/
/* **************************************************************** */
/* */
/* Cross Reference Hacking */
/* */
/* **************************************************************** */
char *
get_xref_token ()
{
char *string;
get_until_in_braces (",", &string);
if (curchar () == ',')
input_text_offset++;
fix_whitespace (string);
normalize_node_name (string);
return (string);
}
int px_ref_flag = 0; /* Controls initial output string. */
/* Make a cross reference. */
VOID
cm_xref (arg)
int arg;
{
if (arg == START)
{
char *arg1, *arg2, *arg3, *arg4, *arg5;
arg1 = get_xref_token ();
arg2 = get_xref_token ();
arg3 = get_xref_token ();
arg4 = get_xref_token ();
arg5 = get_xref_token ();
add_word_args ("%s", px_ref_flag ? "*note " : "*Note ");
if (*arg5 || *arg4)
{
add_word_args ("%s: (%s)%s", arg2, arg4, arg1);
return;
}
else
remember_node_reference (arg1, line_number, followed_reference);
if (*arg3)
{
if (!*arg2)
{
add_word_args ("%s: %s", arg3, arg1);
}
else
{
add_word_args ("%s: %s", arg2, arg1);
}
return;
}
if (*arg2)
{
execute_string ("%s", arg2);
add_word_args (": %s", arg1);
}
else
{
add_word_args ("%s::", arg1);
}
}
else
{
/* Check to make sure that the next non-whitespace character is either
a period or a comma. input_text_offset is pointing at the "}" which
ended the xref or pxref command. */
LONG temp = input_text_offset + 1;
if (output_paragraph[output_paragraph_offset - 2] == ':' &&
output_paragraph[output_paragraph_offset - 1] == ':')
return;
while (temp < size_of_input_text)
{
if (cr_or_whitespace (input_text[temp]))
temp++;
else
{
if (input_text[temp] == '.' ||
input_text[temp] == ',' ||
input_text[temp] == '\t')
return;
else
{
line_error ("Cross-reference must be terminated with a period or a comma");
return;
}
}
}
}
}
VOID
cm_pxref (arg)
int arg;
{
if (arg == START)
{
px_ref_flag++;
cm_xref (arg);
px_ref_flag--;
}
else
add_char ('.');
}
VOID
cm_inforef (arg)
int arg;
{
if (arg == START)
{
char *node, *pname, *file;
node = get_xref_token ();
pname = get_xref_token ();
file = get_xref_token ();
add_word_args ("*note %s: (%s)%s", pname, file, node);
}
}
/* **************************************************************** */
/* */
/* Insertion Command Stubs */
/* */
/* **************************************************************** */
VOID
cm_quotation ()
{
begin_insertion (quotation);
}
VOID
cm_example ()
{
begin_insertion (example);
}
VOID
cm_smallexample ()
{
begin_insertion (smallexample);
}
VOID
cm_lisp ()
{
begin_insertion (lisp);
}
VOID
cm_format ()
{
begin_insertion (format);
}
VOID
cm_display ()
{
begin_insertion (display);
}
VOID
cm_itemize ()
{
begin_insertion (itemize);
}
VOID
cm_enumerate ()
{
begin_insertion (enumerate);
}
VOID
cm_table ()
{
begin_insertion (table);
}
VOID
cm_group ()
{
begin_insertion (group);
}
VOID
cm_ifinfo ()
{
begin_insertion (ifinfo);
}
VOID
cm_tex ()
{
discard_until ("\n@end tex");
discard_until ("\n");
}
VOID
cm_iftex ()
{
discard_until ("\n@end iftex");
discard_until ("\n");
}
VOID
cm_titlespec ()
{
discard_until ("\n@end titlespec");
discard_until ("\n");
}
VOID
cm_titlepage ()
{
discard_until ("\n@end titlepage");
discard_until ("\n");
}
VOID
cm_ignore ()
{
discard_until ("\n@end ignore");
discard_until ("\n");
}
/* **************************************************************** */
/* */
/* @itemx, @item */
/* */
/* **************************************************************** */
/* Non-zero means a string is in execution, as opposed to a file. */
int executing_string = 0;
/* Execute the string produced by formatting the ARGs with FORMAT. This
is like submitting a new file with @include. */
#ifdef MSDOS
VOID CDECL
execute_string (char *format, ...)
{
static char temp_string[4000];
va_list arg_ptr;
va_start (arg_ptr, format);
vsprintf (temp_string, format, arg_ptr);
strcat (temp_string, "@bye\n");
pushfile ();
input_text_offset = 0;
input_text = temp_string;
input_filename = savestring (input_filename);
size_of_input_text = strlen (temp_string);
executing_string++;
reader_loop ();
popfile ();
executing_string--;
free_and_clear (&command);
command = savestring ("not bye");
}
#else /* not MSDOS */
VOID
execute_string (format, arg1, arg2, arg3, arg4, arg5)
char *format;
{
static char temp_string[4000];
sprintf (temp_string, format, arg1, arg2, arg3, arg4, arg5);
strcat (temp_string, "@bye\n");
pushfile ();
input_text_offset = 0;
input_text = temp_string;
input_filename = savestring (input_filename);
size_of_input_text = strlen (temp_string);
executing_string++;
reader_loop ();
popfile ();
executing_string--;
free_and_clear (&command);
command = savestring ("not bye");
}
#endif /* not MSDOS */
int itemx_flag = 0;
VOID
cm_itemx ()
{
itemx_flag++;
cm_item ();
itemx_flag--;
}
VOID
cm_item ()
{
char *rest_of_line, *item_func;
/* Can only hack "@item" while inside of an insertion. */
if (insertion_level)
{
get_until ("\n", &rest_of_line);
canon_white (rest_of_line);
item_func = current_item_function ();
/* Okay, do the right thing depending on which insertion function
is active. */
switch (current_insertion_type ())
{
case menu:
case quotation:
case example:
case smallexample:
case lisp:
case format:
case display:
case group:
case ifinfo:
line_error ("The `@%s' command is meaningless within a `@%s' block",
command,
insertion_type_pname (current_insertion_type ()));
break;
case itemize:
case enumerate:
if (itemx_flag)
{
line_error ("@itemx is not meaningful inside of a `%s' block",
insertion_type_pname (current_insertion_type ()));
}
else
{
start_paragraph ();
kill_self_indent (-1);
discard_until ("\n");
filling_enabled = indented_fill = true;
if (current_insertion_type () == itemize)
{
indent (output_column = current_indent - 2);
/* I need some way to determine whether this command
takes braces or not. I believe the user can type
either "@bullet" or "@bullet{}". Of course, they
can also type "o" or "#" or whatever else they want. */
if (item_func && *item_func)
{
if (*item_func == '@')
if (item_func[strlen (item_func) - 1] != '}')
execute_string ("%s{}", item_func);
else
execute_string ("%s", item_func);
else
execute_string ("%s", item_func);
}
insert (' ');
output_column++;
}
else
number_item ();
/* Special hack. This makes close paragraph ignore you until
the start_paragraph () function has been called. */
must_start_paragraph = 1;
}
break;
case table:
{
/* Get rid of extra characters. */
kill_self_indent (-1);
/* close_paragraph () almost does what we want. The problem
is when paragraph_is_open, and last_char_was_newline, and
the last newline has been turned into a space, because
filling_enabled. I handle it here. */
if (last_char_was_newline && filling_enabled && paragraph_is_open)
insert ('\n');
close_paragraph ();
/* Indent on a new line, but back up one indentation level. */
/* force existing indentation. */
add_char ('i');
output_paragraph_offset--;
kill_self_indent (default_indentation_increment + 1);
/* Add item's argument to the line. */
filling_enabled = false;
if (!item_func && !(*item_func))
execute_string ("%s", rest_of_line);
else
execute_string ("%s{%s}", item_func, rest_of_line);
/* Start a new line, and let start_paragraph ()
do the indenting of it for you. */
close_single_paragraph ();
indented_fill = filling_enabled = true;
}
}
free (rest_of_line);
}
else
line_error ("@%s found outside of an insertion block", command);
}
/* **************************************************************** */
/* */
/* Defun and Friends */
/* */
/* **************************************************************** */
/* The list of args that were passed to the def**** command. */
char **defun_args = (char **)NULL;
/* An alist mapping defun insertion types to the text that we use
to describe them. */
struct {
enum insertion_type type;
char *title;
} type_title_alist[] = {
{ defun, "Function" },
{ defmac, "Macro" },
{ defspec, "Special form" },
{ defopt, "Option" },
{ deffn, (char *)NULL },
{ defvar, "Variable" },
{ (enum insertion_type)0, (char *)NULL }
};
/* Return the title string for this type of defun. */
char *
defun_title (type)
enum insertion_type type;
{
register int i;
for (i = 0; type_title_alist[i].type || type_title_alist[i].title; i++)
if (type_title_alist[i].type == type)
return (type_title_alist[i].title);
return (char *)NULL;
}
/* Return a list of words from the contents of STRING.
You can group words with braces. */
char **
args_from_string (string)
char *string;
{
char **result = (char **) NULL;
register int i, start, result_index, size;
int len, skip_til_brace = 0;
i = result_index = size = 0;
/* Get a token, and stuff it into RESULT. The tokens are split
at spaces or tabs. */
while (string[i])
{
/* Skip leading whitespace. */
for (; string[i] && whitespace (string[i]); i++);
start = i;
if (!string[i])
return (result);
/* If this is a command which takes it's argument in braces, then
gobble the whole thing. */
if (string[i] == COMMAND_PREFIX)
{
register int j;
for (j = i; string[j] &&
!whitespace (string[j]) &&
string[j] != '{'; j++);
if (string[j] == '{')
{
while (string[j] && string[j] != '}')
j++;
if (string[j])
j++;
i = j;
goto add_arg;
}
}
if (string[i] == '{' && !whitespace (string[i + 1]))
{
skip_til_brace = 1;
start = ++i;
}
/* Skip until whitespace or close brace. */
while (string[i] &&
((skip_til_brace && string[i] != '}') ||
(!skip_til_brace && !whitespace (string[i]))))
i++;
add_arg:
len = i - start;
if (result_index + 2 >= size)
{
if (!size)
result = (char **) xmalloc ((size = 10) * (sizeof (char *)));
else
result =
(char **) xrealloc (result, ((size += 10) * (sizeof (char *))));
}
result[result_index] = (char *) xmalloc (1 + len);
strncpy (result[result_index], string + start, len);
result[result_index][len] = '\0';
result_index++;
result[result_index] = (char *) NULL;
if (skip_til_brace)
{
skip_til_brace = 0;
if (string[i])
i++;
}
}
return (result);
}
VOID
get_defun_args ()
{
register int i;
char *line;
get_rest_of_line (&line);
if (defun_args)
{
for (i = 0; defun_args[i]; i++)
free (defun_args[i]);
free (defun_args);
}
defun_args = args_from_string (line);
free (line);
}
VOID
insert_defun_arg (string, where)
char *string;
int where;
{
register int i;
for (i = 0; defun_args[i]; i++);
defun_args = (char **)xrealloc (defun_args, (i + 2) * sizeof (char *));
defun_args[i + 1] = (char *)NULL;
for (; i != where; --i)
defun_args[i] = defun_args[i - 1];
defun_args[i] = savestring (string);
}
/* Make the defun type insertion.
TYPE says which insertion this is.
TITLE is the string to describe the object being described, or NULL
for no title string.
X_P says not to start a new insertion if non-zero. */
VOID
defun_internal (type, title, x_p)
enum insertion_type type;
char *title;
int x_p;
{
register int i = 0;
char *type_name, *func_name;
int old_no_indent = no_indent;
get_defun_args ();
if (title)
insert_defun_arg (title, 0);
if (defun_args[0])
{
type_name = defun_args[0];
i++;
if (defun_args[1])
{
func_name = defun_args[1];
i++;
}
else
func_name = "";
}
else
type_name = "";
no_indent = true;
start_paragraph ();
execute_string (" * %s: %s", type_name, func_name);
no_indent = old_no_indent;
for (; defun_args[i]; i++)
{
if (*defun_args[i] == '&')
add_word_args (" %s", defun_args[i]);
else
execute_string (" @var{%s}", defun_args[i]);
}
add_char ('\n');
if (type == defvar || type == defopt)
execute_string ("@vindex %s\n", func_name);
else
execute_string ("@findex %s\n", func_name);
if (!x_p)
begin_insertion (type);
}
/* Add an entry for a function, macro, special form, variable, or option.
If the name of the calling command ends in `x', then this is an extra
entry included in the body of an insertion of the same type. */
VOID
cm_defun ()
{
int x_p;
enum insertion_type type;
char *title, *temp = savestring (command);
x_p = (command[strlen (command) - 1] == 'x');
if (x_p)
temp[strlen (temp) - 1] = '\0';
type = find_type_from_name (temp);
free (temp);
/* If we are adding to an already existing insertion, then make sure
that we are already in an insertion of type TYPE. */
if (x_p &&
(!insertion_level || insertion_stack->insertion != type))
{
line_error ("Must be in a `%s' insertion in order to use `%s'x",
command, command);
return;
}
title = defun_title (type);
defun_internal (type, title, x_p);
}
/* End existing insertion block. */
VOID
cm_end ()
{
char *temp;
enum insertion_type type;
if (!insertion_level)
{
line_error ("Unmatched `@%s'", command);
return;
}
get_rest_of_line (&temp);
canon_white (temp);
if (strlen (temp) == 0)
line_error ("`@%s' needs something after it", command);
type = find_type_from_name (temp);
if (type == bad_type)
{
line_error ("Bad argument to `%s', `%s', using `%s'",
command, temp, insertion_type_pname (current_insertion_type ()));
}
end_insertion (type);
free (temp);
}
/* **************************************************************** */
/* */
/* Other Random Commands */
/* */
/* **************************************************************** */
/* noindent () used to do something valueable, but it didn't follow the
spec for noindent in the texinfo manual. Now it does nothing, which,
in the case of makeinfo, is correct. */
VOID
cm_noindent ()
{
/* no_indent = true;
indented_fill = false; */
}
/* I don't know exactly what to do with this. Should I allow
someone to switch filenames in the middle of output? Since the
file could be partially written, this doesn't seem to make sense.
Another option: ignore it, since they don't *really* want to
switch files. Finally, complain, or at least warn. */
VOID
cm_setfilename ()
{
char *filename;
get_rest_of_line (&filename);
/* warning ("`@%s %s' encountered and ignored", command, filename); */
free (filename);
}
VOID
cm_comment ()
{
discard_until ("\n");
}
VOID
cm_br ()
{
close_paragraph ();
}
/* Insert the number of blank lines passed as argument. */
VOID
cm_sp ()
{
int lines;
char *line;
close_paragraph ();
get_rest_of_line (&line);
sscanf (line, "%d", &lines);
while (lines--)
add_char ('\n');
free (line);
}
VOID
cm_settitle ()
{
discard_until ("\n");
}
VOID
cm_need ()
{
}
/* Start a new line with just this text on it.
Then center the line of text.
This always ends the current paragraph. */
VOID
cm_center ()
{
char *line;
close_paragraph ();
filling_enabled = indented_fill = false;
get_rest_of_line (&line);
if (strlen (line) < fill_column)
{
int i = (fill_column - strlen (line)) / 2;
while (i--)
insert (' ');
}
add_word_args ("%s", line);
free (line);
insert ('\n');
close_paragraph ();
filling_enabled = true;
}
/* Show what an expression returns. */
VOID
cm_result (arg)
int arg;
{
if (arg == END)
add_word ("=>");
}
/* What an expression expands to. */
VOID
cm_expansion (arg)
int arg;
{
if (arg == END)
add_word ("==>");
}
/* Indicates two expressions are equivalent. */
VOID
cm_equiv (arg)
int arg;
{
if (arg == END)
add_word ("==");
}
/* What an expression may print. */
VOID
cm_print (arg)
int arg;
{
if (arg == END)
add_word ("-|");
}
/* An error signaled. */
VOID
cm_error (arg)
int arg;
{
if (arg == END)
add_word ("error-->");
}
/* The location of point in an example of a buffer. */
VOID
cm_point (arg)
int arg;
{
if (arg == END)
add_word ("-!-");
}
/* Start a new line with just this text on it.
The text is outdented one level if possible. */
VOID
cm_exdent ()
{
char *line;
int i = current_indent;
if (current_indent)
current_indent -= default_indentation_increment;
get_rest_of_line (&line);
close_single_paragraph ();
add_word_args ("%s", line);
current_indent = i;
free (line);
close_single_paragraph ();
}
VOID
cm_include ()
{
cm_infoinclude ();
}
/* Remember this file, and move onto the next. */
VOID
cm_infoinclude ()
{
char *filename;
close_paragraph ();
get_rest_of_line (&filename);
pushfile ();
/* In verbose mode we print info about including another file. */
if (verbose_mode)
{
register int i = 0;
register FSTACK *stack = filestack;
for (i = 0, stack = filestack; stack; stack = stack->next, i++);
i *= 2;
printf ("%*s", i, "");
printf ("%c%s %s\n", COMMAND_PREFIX, command, filename);
fflush (stdout);
}
if (!find_and_load (filename))
{
#ifndef MSDOS
extern char *sys_errlist[];
extern int errno, sys_nerr;
#endif /* not MSDOS */
popfile ();
/* Cannot "@include foo", in line 5 of "/wh/bar". */
line_error ("`%c%s %s': %s", COMMAND_PREFIX, command, filename,
((errno < sys_nerr) ?
sys_errlist[errno] : "Unknown file system error"));
}
free (filename);
}
/* The other side of a malformed expression. */
VOID
misplaced_brace ()
{
line_error ("Misplaced `}'");
}
/* Don't let the filling algorithm insert extra whitespace here. */
VOID
cm_force_abbreviated_whitespace ()
{
}
/* Make the output paragraph end the sentence here, even though it
looks like it shouldn't. This also inserts the character which
invoked it. */
VOID
cm_force_sentence_end ()
{
add_char (META ((*command)));
}
/* Signals end of processing. Easy to make this happen. */
VOID
cm_bye ()
{
input_text_offset = size_of_input_text;
}
VOID
cm_asis ()
{
}
VOID
cm_setchapternewpage ()
{
discard_until ("\n");
}
/* **************************************************************** */
/* */
/* Indexing Stuff */
/* */
/* **************************************************************** */
/* An index element... */
typedef struct index_elt
{
struct index_elt *next;
char *entry; /* The index entry itself. */
char *node; /* The node from whence it came. */
} INDEX_ELT;
/* A list of short-names for each index, and the index to that index in our
index array, the_indices. (Sorry, I couldn't resist.) */
typedef struct
{
char *name;
int index;
} INDEX_ALIST;
INDEX_ALIST **name_index_alist = (INDEX_ALIST **) NULL;
#ifdef MSDOS
INDEX_ALIST *find_index (char *name);
#endif /* MSDOS */
/* An array of pointers. Each one is for a different index. The
"synindex" command changes which array slot is pointed to by a
given "index". */
INDEX_ELT **the_indices = (INDEX_ELT **) NULL;
/* The number of defined indices. */
int defined_indices = 0;
/* We predefine these. */
#define program_index 0
#define function_index 1
#define concept_index 2
#define variable_index 3
#define datatype_index 4
#define key_index 5
VOID
init_indices ()
{
int i;
/* Create the default data structures. */
/* Initialize data space. */
if (!the_indices)
{
the_indices = (INDEX_ELT **) xmalloc ((1 + defined_indices) *
sizeof (INDEX_ELT *));
the_indices[defined_indices] = (INDEX_ELT *) NULL;
name_index_alist = (INDEX_ALIST **) xmalloc ((1 + defined_indices) *
sizeof (INDEX_ALIST *));
name_index_alist[defined_indices] = (INDEX_ALIST *) NULL;
}
/* If there were existing indices, get rid of them now. */
for (i = 0; i < defined_indices; i++)
undefindex (name_index_alist[i]->name);
/* Add the default indices. */
defindex ("pg");
defindex ("fn");
defindex ("cp");
defindex ("vr");
defindex ("tp");
defindex ("ky");
}
/* Find which element in the known list of indices has this name.
Returns -1 if NAME isn't found. */
find_index_offset (name)
char *name;
{
register int i;
for (i = 0; i < defined_indices; i++)
if (name_index_alist[i] &&
stricmp (name, name_index_alist[i]->name) == 0)
return (name_index_alist[i]->index);
return (-1);
}
/* Return a pointer to the entry of (name . index) for this name.
Return -1 if the index doesn't exist. */
INDEX_ALIST *
find_index (name)
char *name;
{
int offset = find_index_offset (name);
if (offset > -1)
return (name_index_alist[offset]);
else
return ((INDEX_ALIST *) - 1);
}
/* Given an index name, return the offset in the_indices of this index,
or -1 if there is no such index. */
translate_index (name)
char *name;
{
INDEX_ALIST *which = find_index (name);
if ((LONG) which > -1)
return (which->index);
else
return (-1);
}
/* Return the index list which belongs to NAME. */
INDEX_ELT *
index_list (name)
char *name;
{
int which = translate_index (name);
if (which < 0)
return ((INDEX_ELT *) - 1);
else
return (the_indices[which]);
}
/* Please release me, let me go... */
VOID
free_index (index)
INDEX_ELT *index;
{
INDEX_ELT *temp;
while ((temp = index) != (INDEX_ELT *) NULL)
{
free (temp->entry);
free (temp->node);
index = index->next;
free (temp);
}
}
/* Flush an index by name. */
undefindex (name)
char *name;
{
int i;
int which = find_index_offset (name);
if (which < 0)
return (which);
i = name_index_alist[which]->index;
free_index (the_indices[i]);
the_indices[i] = (INDEX_ELT *) NULL;
free (name_index_alist[which]->name);
free (name_index_alist[which]);
name_index_alist[which] = (INDEX_ALIST *) NULL;
}
/* Define an index known as NAME. We assign the slot number. */
VOID
defindex (name)
char *name;
{
register int i, slot;
/* If it already exists, flush it. */
undefindex (name);
/* Try to find an empty slot. */
slot = -1;
for (i = 0; i < defined_indices; i++)
if (!name_index_alist[i])
{
slot = i;
break;
}
if (slot < 0)
{
/* No such luck. Make space for another index. */
slot = defined_indices;
defined_indices++;
name_index_alist = (INDEX_ALIST **) xrealloc (name_index_alist,
(1 + defined_indices)
* sizeof (INDEX_ALIST *));
the_indices = (INDEX_ELT **) xrealloc (the_indices,
(1 + defined_indices)
* sizeof (INDEX_ELT *));
}
/* We have a slot. Start assigning. */
name_index_alist[slot] = (INDEX_ALIST *) xmalloc (sizeof (INDEX_ALIST));
name_index_alist[slot]->name = savestring (name);
name_index_alist[slot]->index = slot;
the_indices[slot] = (INDEX_ELT *) NULL;
}
/* Add the arguments to the current index command to the index NAME. */
VOID
index_add_arg (name)
char *name;
{
int which = translate_index (name);
char *index_entry;
/* close_paragraph (); */
get_rest_of_line (&index_entry);
if (which < 0)
{
line_error ("Unknown index reference `%s'", name);
free (index_entry);
}
else
{
INDEX_ELT *new = (INDEX_ELT *) xmalloc (sizeof (INDEX_ELT));
new->next = the_indices[which];
new->entry = index_entry;
new->node = current_node;
the_indices[which] = new;
}
}
#define INDEX_COMMAND_SUFFIX "index"
/* The function which user defined index commands call. */
VOID
gen_index ()
{
char *name = savestring (command);
if (strlen (name) >= strlen ("index"))
name[strlen (name) - strlen ("index")] = '\0';
index_add_arg (name);
free (name);
}
/* Define a new index command. Arg is name of index. */
VOID
cm_defindex ()
{
char *name;
get_rest_of_line (&name);
if ((LONG) find_index (name) > -1)
{
line_error ("Index `%s' already exists", name);
free (name);
return;
}
else
{
char *temp = (char *) alloca (1 + strlen (name) + strlen ("index"));
sprintf (temp, "%sindex", name);
define_user_command (temp, gen_index, 0);
defindex (name);
free (name);
}
}
/* Append LIST2 to LIST1. Return the head of the list. */
INDEX_ELT *
index_append (head, tail)
INDEX_ELT *head, *tail;
{
register INDEX_ELT *t_head = head;
if (!t_head)
return (tail);
while (t_head->next)
t_head = t_head->next;
t_head->next = tail;
return (head);
}
/* Expects 2 args, on the same line. Both are index abbreviations.
Make the first one be a synonym for the second one, i.e. make the
first one have the same index as the second one. */
VOID
cm_synindex ()
{
int redirector, redirectee;
char *temp;
skip_whitespace ();
get_until_in_line (" ", &temp);
redirectee = find_index_offset (temp);
skip_whitespace ();
free_and_clear (&temp);
get_until_in_line (" ", &temp);
redirector = find_index_offset (temp);
free (temp);
if (redirector < 0 || redirectee < 0)
{
line_error ("Unknown index reference");
}
else
{
/* I think that we should let the user make indices synonymous to
each other without any lossage of info. This means that one can
say @synindex cp dt anywhere in the file, and things that used to
be in cp will go into dt. */
INDEX_ELT *i1 = the_indices[redirectee], *i2 = the_indices[redirector];
if (i1 || i2)
{
if (i1)
the_indices[redirectee] = index_append (i1, i2);
else
the_indices[redirectee] = index_append (i2, i1);
}
name_index_alist[redirectee]->index =
name_index_alist[redirector]->index;
}
}
VOID
cm_pindex () /* Pinhead index. */
{
index_add_arg ("pg");
}
VOID
cm_vindex () /* variable index */
{
index_add_arg ("vr");
}
VOID
cm_kindex () /* key index */
{
index_add_arg ("ky");
}
VOID
cm_cindex () /* concept index */
{
index_add_arg ("cp");
}
VOID
cm_findex () /* function index */
{
index_add_arg ("fn");
}
VOID
cm_tindex () /* data type index */
{
index_add_arg ("tp");
}
/* Sorting the index. */
int CDECL
index_element_compare (element1, element2)
INDEX_ELT **element1, **element2;
{
return (strcmp ((*element1)->entry, (*element2)->entry));
}
/* Sort the index passed in INDEX, returning an array of
pointers to elements. The array is terminated with a NULL
pointer. We call qsort because it's supposed to be fast.
I think this looks bad. */
INDEX_ELT **
sort_index (index)
INDEX_ELT *index;
{
INDEX_ELT *temp = index;
INDEX_ELT **array;
int count = 0;
while (temp != (INDEX_ELT *) NULL)
{
count++;
temp = temp->next;
}
/* We have the length. Make an array. */
array = (INDEX_ELT **) xmalloc ((count + 1) * sizeof (INDEX_ELT *));
count = 0;
temp = index;
while (temp != (INDEX_ELT *) NULL)
{
array[count++] = temp;
temp = temp->next;
}
array[count] = (INDEX_ELT *) NULL; /* terminate the array. */
/* Sort the array. */
qsort (array, count, sizeof (INDEX_ELT *), index_element_compare);
return (array);
}
/* Takes one arg, a short name of an index to print.
Outputs a menu of the sorted elements of the index. */
VOID
cm_printindex ()
{
int item;
INDEX_ELT *index;
INDEX_ELT **array;
char *index_name;
int old_inhibitions = inhibit_paragraph_indentation;
boolean previous_filling_enabled_value = filling_enabled;
close_paragraph ();
get_rest_of_line (&index_name);
index = index_list (index_name);
if ((LONG) index < 0)
{
line_error ("Unknown index name `%s'", index_name);
free (index_name);
return;
}
else
free (index_name);
array = sort_index (index);
filling_enabled = false;
inhibit_paragraph_indentation = 1;
close_paragraph ();
add_word ("* Menu:\n\n");
for (item = 0; (index = array[item]); item++)
{
execute_string ("* %s: %s.\n", index->entry, index->node);
flush_output ();
}
free (array);
close_paragraph ();
filling_enabled = previous_filling_enabled_value;
inhibit_paragraph_indentation = old_inhibitions;
}
/* **************************************************************** */
/* */
/* Making User Defined Commands */
/* */
/* **************************************************************** */
VOID
define_user_command (name, proc, needs_braces_p)
char *name;
FUNCTION *proc;
int needs_braces_p;
{
int slot = user_command_array_len;
user_command_array_len++;
if (!user_command_array)
user_command_array = (COMMAND **) xmalloc (1 * sizeof (COMMAND *));
user_command_array = (COMMAND **) xrealloc (user_command_array,
(1 + user_command_array_len) *
sizeof (COMMAND *));
user_command_array[slot] = (COMMAND *) xmalloc (sizeof (COMMAND));
user_command_array[slot]->name = savestring (name);
user_command_array[slot]->proc = proc;
user_command_array[slot]->argument_in_braces = needs_braces_p;
}
/* Make ALIAS run the named FUNCTION. Copies properties from FUNCTION. */
VOID
define_alias (alias, function)
char *alias, *function;
{
}
/* Some support for footnotes. */
/* Footnotes are a new construct in Info. We don't know the best method
of implementing them for sure, so we present two possiblities.
MN 1) Make them look like followed references, with the reference
destinations in a makeinfo manufactured node or,
BN 2) Make them appear at the bottom of the node that they originally
appeared in.
*/
#define MN 0
#define BN 1
int footnote_style = MN;
boolean first_footnote_this_node = true;
int footnote_count = 0;
/* Set the footnote style based on he style identifier in STRING. */
VOID
set_footnote_style (string)
char *string;
{
if (stricmp (string, "MN") == 0)
{
footnote_style = MN;
return;
}
if (stricmp (string, "BN") == 0)
{
footnote_style = BN;
return;
}
}
typedef struct fn
{
struct fn *next;
char *marker;
char *note;
} FN;
FN *pending_notes = (FN *) NULL;
/* A method for remembering footnotes. Note that this list gets output
at the end of the current node. */
VOID
remember_note (marker, note)
char *marker, *note;
{
FN *temp = (FN *) xmalloc (sizeof (FN));
temp->marker = savestring (marker);
temp->note = savestring (note);
temp->next = pending_notes;
pending_notes = temp;
footnote_count++;
}
/* How to get rid of existing footnotes. */
VOID
free_pending_notes ()
{
FN *temp;
while ((temp = pending_notes) != (FN *) NULL)
{
free (temp->marker);
free (temp->note);
pending_notes = pending_notes->next;
free (temp);
}
first_footnote_this_node = true;
footnote_count = 0;
}
/* What to do when you see a @footnote construct. */
/* Handle a "footnote".
footnote *{this is a footnote}
where "*" is the marker character for this note. */
VOID
cm_footnote ()
{
char *marker;
char *note;
get_until ("{", &marker);
canon_white (marker);
/* Read the argument in braces. */
if (curchar () != '{')
{
line_error ("`@%s' expected more than just `%s'. It needs something in `{...}'", command, marker);
free (marker);
return;
}
else
{
int braces = 1;
LONG temp = ++input_text_offset;
int len;
while (braces)
{
if (temp == size_of_input_text)
{
line_error ("No closing brace for footnote `%s'", marker);
return;
}
if (input_text[temp] == '{')
braces++;
else if (input_text[temp] == '}')
braces--;
temp++;
}
#ifdef MSDOS
assert (temp - input_text_offset < (1L<<16));
len = (size_t) (temp - input_text_offset) - 1;
#else /* not MSDOS */
len = (temp - input_text_offset) - 1;
#endif /* not MSDOS */
note = xmalloc (len + 1);
strncpy (note, &input_text[input_text_offset], len);
note[len] = '\0';
input_text_offset = temp;
}
if (!current_node || !*current_node)
{
line_error ("Footnote defined without parent node");
free (marker);
free (note);
return;
}
remember_note (marker, note);
switch (footnote_style)
{ /* your method should at least insert marker. */
case MN:
add_word_args ("(%s)", marker);
if (first_footnote_this_node)
{
char *temp_string = xmalloc ((strlen (current_node))
+ (strlen ("-Footnotes")) + 1);
add_word_args (" (*note %s-Footnotes::)", current_node);
strcpy (temp_string, current_node);
strcat (temp_string, "-Footnotes");
remember_node_reference (temp_string, line_number, followed_reference);
free (temp_string);
first_footnote_this_node = false;
}
break;
case BN:
add_word_args ("(%s)", marker);
break;
default:
break;
}
free (marker);
free (note);
}
/* Non-zero means that we are currently in the process of outputting
footnotes. */
int already_outputting_pending_notes = 0;
/* Output the footnotes. We are at the end of the current node. */
VOID
output_pending_notes ()
{
FN *footnote = pending_notes;
if (!pending_notes)
return;
switch (footnote_style)
{
case MN:
{
char *old_current_node = current_node;
char *old_command = savestring (command);
already_outputting_pending_notes++;
execute_string ("@node %s-Footnotes,,,%s\n", current_node, current_node);
already_outputting_pending_notes--;
current_node = old_current_node;
free (command);
command = old_command;
}
break;
case BN:
close_paragraph ();
execute_string ("---------- Footnotes ----------\n\n");
break;
}
/* Handle the footnotes in reverse order. */
{
FN **array = (FN **) xmalloc ((footnote_count + 1) * sizeof (FN *));
array[footnote_count] = (FN *) NULL;
while (--footnote_count > -1)
{
array[footnote_count] = footnote;
footnote = footnote->next;
}
filling_enabled = true;
indented_fill = true;
while (footnote = array[++footnote_count])
{
switch (footnote_style)
{
case MN:
case BN:
execute_string ("(%s) %s", footnote->marker, footnote->note);
close_paragraph ();
break;
}
}
close_paragraph ();
free (array);
}
}
#ifdef MSDOS
void _huge *
xhalloc (long size)
{
void _huge *value = (void _huge *) halloc (size, (size_t) 1);
if (value == (void HUGE *) 0 )
{
error ("Virtual memory exhausted! Needed %ld bytes.", size);
exit (FATAL);
}
return value;
}
#if 0
/* Here we do a huge "realloc" by allocating a new block and
moving the old block afterwards. This is *slow*, but should
be reliable. */
void _huge *
xhrealloc (void _huge *ptr, long new_size, long old_size)
{
void _huge *value = (void _huge *) halloc (new_size, (size_t)1 );
if (!value)
{
error ("Virtual memory exhausted in realloc ().");
abort ();
}
else
{
char _huge *dest = value;
char _huge *src = ptr;
while (old_size > 0L)
{
unsigned int bytes = (unsigned int) min (0x8000L, old_size);
memcpy (dest, src, bytes);
old_size -= (long) bytes;
dest += (long) bytes;
src += (long) bytes;
}
}
hfree (ptr);
return value;
}
#endif /* not needed */
long
hread (int fd, void _huge *buffer, long bytes)
{
long bytes_read = 0L;
while (1)
{
int n = read (fd, buffer, (unsigned int) min (0x4000L, bytes));
if (n > 0)
{
bytes_read += (long) n;
bytes -= (long) n;
/* we can't say buffer += n here, because we have to make
sure that the offset of BUFFER doesn't overflow during
the read() system call. Therefore we add what we read
to the segment of BUFFER. */
FP_SEG(buffer) += (n >> 4);
FP_OFF(buffer) += (n & 0x0F);
}
else if (n == 0)
return bytes_read; /* done */
else
{
perror ("Can't read input");
exit (FATAL);
}
}
}
#endif /* MSDOS */
/*
* Local Variables:
* mode:C
* ChangeLog:ChangeLog
* End:
*/